home *** CD-ROM | disk | FTP | other *** search
/ Micromanía 92 / CDMM92_1.ISO / SOF 2 SDK / sof2sdk-101.msi / _92D6AC311BB48EBA344BBABC89DA6AB0 / _6B9633D7EA90447B9B8CB999114E5F4D < prev    next >
Encoding:
Text File  |  2002-07-01  |  45.9 KB  |  1,904 lines

  1. // Copyright (C) 2001-2002 Raven Software
  2. //
  3. #include "g_local.h"
  4.  
  5. // g_client.c -- client functions that don't happen every frame
  6.  
  7. static vec3_t    playerMins = {-15, -15, -46};
  8. static vec3_t    playerMaxs = {15, 15, 48};
  9.  
  10.  
  11. /*
  12. ================
  13. G_AddClientSpawn
  14.  
  15. adds a spawnpoint to the spawnpoint array using the given entity for
  16. origin and angles as well as the team for filtering teams.
  17. ================
  18. */
  19. void G_AddClientSpawn ( gentity_t* ent, team_t team )
  20. {
  21.     static vec3_t    mins = {-15,-15,-45};
  22.     static vec3_t    maxs = {15,15,46};
  23.     vec3_t            end;
  24.     trace_t            tr;
  25.  
  26.     // Drop it to the ground, and if it starts solid just throw it out
  27.     VectorCopy ( ent->s.origin, end );
  28.     end[2] -= 1024;
  29.  
  30.     tr.fraction = 0.0f;
  31.     trap_Trace ( &tr, ent->s.origin, mins, maxs, end, ent->s.number, MASK_SOLID );
  32.  
  33.     // We are only looking for terrain collisions at this point
  34.     if ( tr.contents & CONTENTS_TERRAIN )
  35.     {
  36.         // If its in the ground then throw it awway
  37.         if ( tr.startsolid )
  38.         {
  39.             return;
  40.         }
  41.         // Drop it down to the ground now
  42.         else if ( tr.fraction < 1.0f && tr.fraction > 0.0f )
  43.         {
  44.             VectorCopy ( tr.endpos, ent->s.origin );
  45.             ent->s.origin[2] += 1.0f;
  46.             tr.startsolid = qfalse;
  47.         }
  48.     }
  49.  
  50.     if ( tr.startsolid )
  51.     {
  52.         Com_Printf ( S_COLOR_YELLOW "WARNING: gametype_player starting in solid at %.2f,%.2f,%.2f\n", ent->s.origin[0], ent->s.origin[1], ent->s.origin[2] );
  53.     }
  54.  
  55.     level.spawns[level.spawnCount].team = team;
  56.  
  57.     // Release the entity and store the spawn in its own array
  58.     VectorCopy ( ent->s.origin, level.spawns[level.spawnCount].origin );
  59.     VectorCopy ( ent->s.angles, level.spawns[level.spawnCount].angles );
  60.  
  61.     // Increase the spawn count
  62.     level.spawnCount++;
  63. }
  64.  
  65. /*QUAKED info_player_deathmatch (1 0 1) (-16 -16 -46) (16 16 48) initial
  66. potential spawning position for deathmatch games.
  67. The first time a player enters the game, they will be at an 'initial' spot.
  68. Targets will be fired when someone spawns in on them.
  69. */
  70. void SP_info_player_deathmatch( gentity_t *ent ) 
  71. {
  72.     // Cant take any more spawns!!
  73.     if ( level.spawnCount >= MAX_SPAWNS )
  74.     {
  75.         G_FreeEntity ( ent );
  76.         return;
  77.     }
  78.  
  79.     G_AddClientSpawn ( ent, TEAM_FREE );
  80.  
  81.     G_FreeEntity ( ent );
  82. }
  83.  
  84. /*QUAKED info_player_intermission (1 0 1) (-16 -16 -46) (16 16 48)
  85. The intermission will be viewed from this point.  Target an info_notnull for the view direction.
  86. */
  87. void SP_info_player_intermission( gentity_t *ent ) 
  88. {
  89. }
  90.  
  91. /*
  92. ================
  93. G_SpotWouldTelefrag
  94. ================
  95. */
  96. qboolean G_SpotWouldTelefrag( gspawn_t* spawn ) 
  97. {
  98.     int            i, num;
  99.     int            touch[MAX_GENTITIES];
  100.     gentity_t    *hit;
  101.     vec3_t        mins, maxs;
  102.  
  103.     VectorAdd( spawn->origin, playerMins, mins );
  104.     VectorAdd( spawn->origin, playerMaxs, maxs );
  105.     num = trap_EntitiesInBox( mins, maxs, touch, MAX_GENTITIES );
  106.  
  107.     for (i=0 ; i<num ; i++) 
  108.     {
  109.         hit = &g_entities[touch[i]];
  110.  
  111.         if ( hit->client) 
  112.         {
  113.             if ( G_IsClientSpectating ( hit->client ) )
  114.             {
  115.                 continue;
  116.             }
  117.  
  118.             if ( G_IsClientDead ( hit->client ) )
  119.             {
  120.                 continue;
  121.             }
  122.  
  123.             return qtrue;
  124.         }
  125.     }
  126.  
  127.     return qfalse;
  128. }
  129.  
  130. /*
  131. ================
  132. G_SelectRandomSpawnPoint
  133.  
  134. go to a random point that doesn't telefrag
  135. ================
  136. */
  137. gspawn_t* G_SelectRandomSpawnPoint ( team_t team ) 
  138. {    
  139.     int            i;
  140.     int            count;
  141.     int            tfcount;
  142.     gspawn_t    *spawns[MAX_SPAWNS];
  143.     gspawn_t    *tfspawns[MAX_SPAWNS];
  144.  
  145.     count = 0;
  146.     tfcount = 0;
  147.  
  148.     for ( i = 0; i < level.spawnCount; i ++ )
  149.     {
  150.         gspawn_t* spawn = &level.spawns[i];
  151.  
  152.         if ( team != -1 && team != spawn->team )
  153.         {
  154.             continue;
  155.         }
  156.  
  157.         if ( G_SpotWouldTelefrag( spawn ) ) 
  158.         {
  159.             tfspawns[tfcount++] = spawn;
  160.             continue;
  161.         }
  162.  
  163.         spawns[ count++ ] = spawn;
  164.     }
  165.  
  166.     // no spots that won't telefrag so just pick one that will
  167.     if ( !count )
  168.     {    
  169.         // No telefrag spots, just return NULL since there is no more to find
  170.         if ( !tfcount ) 
  171.         {
  172.             return NULL;
  173.         }
  174.  
  175.         // telefrag someone
  176.         return tfspawns[ rand() % tfcount ];
  177.     }
  178.  
  179.     return spawns[ rand() % count ];
  180. }
  181.  
  182. /*
  183. ===========
  184. G_SelectRandomSafeSpawnPoint
  185.  
  186. Select a random spawn point that is safe for the client to spawn at.  A safe spawn point 
  187. is one that is at least a certain distance from another client.
  188. ============
  189. */
  190. gspawn_t* G_SelectRandomSafeSpawnPoint ( team_t team, float safeDistance )
  191. {
  192.     gspawn_t*    spawns[MAX_SPAWNS];
  193.     float        safeDistanceSquared;
  194.     int            count;
  195.     int            i;
  196.  
  197.     // Square the distance for faster comparisons
  198.     safeDistanceSquared = safeDistance * safeDistance;
  199.  
  200.     // Build a list of spawns
  201.     for ( i = 0, count = 0; i < level.spawnCount; i ++ )
  202.     {
  203.         gspawn_t* spawn = &level.spawns[i];
  204.         int          j;
  205.  
  206.         // Ensure the team matches
  207.         if ( team != -1 && team != spawn->team )
  208.         {
  209.             continue;
  210.         }
  211.  
  212.         // Make sure this spot wont kill another player
  213.         if ( G_SpotWouldTelefrag( spawn ) ) 
  214.         {
  215.             continue;
  216.         }
  217.  
  218.         // Loop through connected clients 
  219.         for ( j = 0; j < level.numConnectedClients && count < MAX_SPAWNS; j ++ )
  220.         {
  221.             gentity_t* other = &g_entities[level.sortedClients[j]];
  222.             vec3_t       diff;
  223.  
  224.             if ( other->client->pers.connected != CON_CONNECTED )
  225.             {
  226.                 continue;
  227.             }
  228.  
  229.             // Skip clients that are spectating or dead
  230.             if ( G_IsClientSpectating ( other->client ) || G_IsClientDead ( other->client ) )
  231.             {
  232.                 continue;
  233.             }
  234.  
  235.             // on safe team, dont count this guy
  236.             if ( level.gametypeData->teams && team == other->client->sess.team )
  237.             {
  238.                 continue;
  239.             }
  240.  
  241.             VectorSubtract ( other->r.currentOrigin, spawn->origin, diff );
  242.  
  243.             // Far enough away to qualify
  244.             if ( VectorLengthSquared ( diff ) < safeDistanceSquared )
  245.             {
  246.                 break;
  247.             }
  248.         }
  249.  
  250.         // If we didnt go through the whole list of clients then we must
  251.         // have hit one that was too close.  But if we did go through teh whole
  252.         // list then this spawn point is good to go
  253.         if ( j >= level.numConnectedClients )
  254.         {
  255.             spawns[count++] = spawn;
  256.         }    
  257.     }
  258.  
  259.     // Nothing found, try it at half the safe distance
  260.     if ( !count )
  261.     {
  262.         // Gotta stop somewhere
  263.         if ( safeDistance / 2 < 250 )
  264.         {
  265.             return G_SelectRandomSpawnPoint ( team );
  266.         }
  267.         else
  268.         {
  269.             return G_SelectRandomSafeSpawnPoint ( team, safeDistance / 2 );
  270.         }
  271.     }
  272.  
  273.     // Spawn them at one of the spots
  274.     return spawns[ rand() % count ];
  275. }
  276.  
  277. /*
  278. ===========
  279. G_SelectSpectatorSpawnPoint
  280. ============
  281. */
  282. gspawn_t* G_SelectSpectatorSpawnPoint( void ) 
  283. {
  284.     static gspawn_t spawn;
  285.  
  286.     FindIntermissionPoint();
  287.  
  288.     VectorCopy( level.intermission_origin, spawn.origin );
  289.     VectorCopy( level.intermission_angle, spawn.angles );
  290.  
  291.     return &spawn;
  292. }
  293.  
  294. /*
  295. ===============
  296. G_InitBodyQueue
  297. ===============
  298. */
  299. void G_InitBodyQueue (void) 
  300. {
  301.     gentity_t    *ent;
  302.     int            max;
  303.  
  304.     if ( level.gametypeData->respawnType == RT_NONE )
  305.     {
  306.         level.bodySinkTime = 0;
  307.         max = BODY_QUEUE_SIZE_MAX;
  308.     }
  309.     else
  310.     {
  311.         level.bodySinkTime = BODY_SINK_DELAY;
  312.         max = BODY_QUEUE_SIZE;
  313.     }
  314.  
  315.     level.bodyQueIndex = 0;
  316.     for ( level.bodyQueSize = 0; 
  317.           level.bodyQueSize < max && level.bodyQueSize < level.maxclients; 
  318.           level.bodyQueSize++) 
  319.     {
  320.         ent = G_Spawn();
  321.         ent->classname = "bodyque";
  322.         ent->neverFree = qtrue;
  323.         level.bodyQue[level.bodyQueSize] = ent;
  324.     }
  325. }
  326.  
  327. /*
  328. =============
  329. BodySink
  330.  
  331. After sitting around for five seconds, fall into the ground and dissapear
  332. =============
  333. */
  334. void BodySink( gentity_t *ent ) 
  335. {
  336.     if ( level.time - ent->timestamp > level.bodySinkTime + BODY_SINK_TIME ) 
  337.     {
  338.         // the body ques are never actually freed, they are just unlinked
  339.         trap_UnlinkEntity( ent );
  340.         ent->physicsObject = qfalse;
  341.         return;    
  342.     }
  343.  
  344.     ent->s.eFlags |= EF_NOSHADOW;
  345.  
  346.     ent->nextthink = level.time + 100;
  347.     ent->s.pos.trBase[2] -= 1;
  348. }
  349.  
  350. /*
  351. =============
  352. CopyToBodyQue
  353.  
  354. A player is respawning, so make an entity that looks
  355. just like the existing corpse to leave behind.
  356. =============
  357. */
  358. void CopyToBodyQue( gentity_t *ent, int hitLocation, vec3_t direction ) 
  359. {
  360.     gentity_t    *body;
  361.     int            contents;
  362.     int            parm;
  363.  
  364.     trap_UnlinkEntity (ent);
  365.  
  366.     // if client is in a nodrop area, don't leave the body
  367.     contents = trap_PointContents( ent->r.currentOrigin, -1 );
  368.     if ( contents & CONTENTS_NODROP ) 
  369.     {
  370.         return;
  371.     }
  372.  
  373.     // grab a body que and cycle to the next one
  374.     body = level.bodyQue[ level.bodyQueIndex ];
  375.     level.bodyQueIndex = (level.bodyQueIndex + 1) % level.bodyQueSize;
  376.  
  377.     trap_UnlinkEntity (body);
  378.  
  379.     body->s                    = ent->s;
  380.     body->s.eType            = ET_BODY;
  381.     body->s.eFlags            = EF_DEAD;
  382.     body->s.gametypeitems    = 0;
  383.     body->s.loopSound        = 0;
  384.     body->s.number            = body - g_entities;
  385.     body->timestamp            = level.time;
  386.     body->physicsObject        = qtrue;
  387.     body->physicsBounce        = 0;
  388.     body->s.otherEntityNum  = ent->s.clientNum;
  389.  
  390.     if ( body->s.groundEntityNum == ENTITYNUM_NONE ) 
  391.     {
  392.         body->s.pos.trType = TR_GRAVITY;
  393.         body->s.pos.trTime = level.time;
  394.         VectorCopy( ent->client->ps.velocity, body->s.pos.trDelta );
  395.     } 
  396.     else 
  397.     {
  398.         body->s.pos.trType = TR_STATIONARY;
  399.     }
  400.     
  401.     body->s.event = 0;
  402.  
  403.     parm  = (DirToByte( direction )&0xFF);
  404.     parm += (hitLocation<<8);
  405.     G_AddEvent(body, EV_BODY_QUEUE_COPY, parm);
  406.  
  407.     body->r.svFlags = ent->r.svFlags | SVF_BROADCAST;
  408.     VectorCopy (ent->r.mins, body->r.mins);
  409.     VectorCopy (ent->r.maxs, body->r.maxs);
  410.     VectorCopy (ent->r.absmin, body->r.absmin);
  411.     VectorCopy (ent->r.absmax, body->r.absmax);
  412.  
  413.     body->s.torsoAnim = body->s.legsAnim = ent->client->ps.legsAnim & ~ANIM_TOGGLEBIT;
  414.  
  415.     body->clipmask = CONTENTS_SOLID | CONTENTS_PLAYERCLIP;
  416.     body->r.contents = 0; // CONTENTS_CORPSE;
  417.     body->r.ownerNum = ent->s.number;
  418.  
  419.     if ( level.bodySinkTime )
  420.     {
  421.         body->nextthink = level.time + level.bodySinkTime;
  422.         body->think = BodySink;
  423.         body->s.time2 = 0;
  424.     }
  425.     else
  426.     {
  427.         // Store the time the body was spawned so the client can make them
  428.         // dissapear if need be.
  429.         body->s.time2 = level.time;
  430.     }
  431.  
  432.     body->die = body_die;
  433.     body->takedamage = qtrue;
  434.  
  435.     body->s.apos.trBase[PITCH] = 0;
  436.  
  437.     body->s.pos.trBase[2] = ent->client->ps.origin[2];
  438.     VectorCopy ( body->s.pos.trBase, body->r.currentOrigin );
  439.  
  440.     trap_LinkEntity (body);
  441. }
  442.  
  443. //======================================================================
  444.  
  445.  
  446. /*
  447. ==================
  448. SetClientViewAngle
  449.  
  450. ==================
  451. */
  452. void SetClientViewAngle( gentity_t *ent, vec3_t angle ) 
  453. {
  454.     int            i;
  455.  
  456.     // set the delta angle
  457.     for (i=0 ; i<3 ; i++) 
  458.     {
  459.         int        cmdAngle;
  460.  
  461.         cmdAngle = ANGLE2SHORT(angle[i]);
  462.         ent->client->ps.delta_angles[i] = cmdAngle - ent->client->pers.cmd.angles[i];
  463.     }
  464.  
  465.     VectorCopy( angle, ent->s.angles );
  466.     VectorCopy (ent->s.angles, ent->client->ps.viewangles);
  467. }
  468.  
  469. /*
  470. ================
  471. G_SetRespawnTimer
  472. ================
  473. */
  474. void G_SetRespawnTimer ( gentity_t* ent )
  475. {
  476.     // Start a new respawn interval if the old one has passed
  477.     if ( level.time > level.gametypeRespawnTime[ent->client->sess.team] )
  478.     {
  479.         level.gametypeRespawnTime[ent->client->sess.team] = level.time + g_respawnInterval.integer * 1000;
  480.     }
  481.  
  482.     // start the interval if its not already started
  483.     ent->client->ps.respawnTimer = level.gametypeRespawnTime[ent->client->sess.team] + 1000;
  484. }
  485.  
  486. /*
  487. ================
  488. respawn
  489. ================
  490. */
  491. void respawn( gentity_t *ent )
  492. {
  493.     gentity_t    *tent;
  494.     qboolean    ghost = qfalse;
  495.  
  496.     // No respawning when intermission is queued
  497.     if ( level.intermissionQueued )
  498.     {
  499.         return;
  500.     }
  501.  
  502.     // When we get here the user has just accepted their fate and now
  503.     // needs to wait for the ability to respawn
  504.     switch ( level.gametypeData->respawnType )
  505.     {
  506.         case RT_INTERVAL:
  507.             G_SetRespawnTimer ( ent );
  508.             ghost = qtrue;
  509.             break;
  510.  
  511.         case RT_NONE:
  512.  
  513.             // Turn into a ghost
  514.             ghost = qtrue;
  515.             break;
  516.     }
  517.  
  518.     // If they are a ghost then give a health point, but dont respawn
  519.     if ( ghost )
  520.     {
  521.         G_StartGhosting ( ent );
  522.         return;
  523.     }
  524.  
  525.     trap_UnlinkEntity (ent);
  526.     ClientSpawn(ent);
  527.  
  528.     // Add a teleportation effect.
  529.     tent = G_TempEntity( ent->client->ps.origin, EV_PLAYER_TELEPORT_IN );
  530.     tent->s.clientNum = ent->s.clientNum;
  531. }
  532.  
  533. /*
  534. ================
  535. G_GhostCount
  536.  
  537. Returns number of ghosts on a team, if -1 is given for a team all ghosts in the game
  538. are returned instead
  539. ================
  540. */
  541. int G_GhostCount ( team_t team )
  542. {
  543.     int i;
  544.     int count;
  545.  
  546.     for ( i = 0, count = 0; i < level.numConnectedClients; i ++ )    
  547.     {
  548.         if (g_entities[level.sortedClients[i]].client->pers.connected != CON_CONNECTED )
  549.         {
  550.             continue;
  551.         }
  552.  
  553.         if ( g_entities[level.sortedClients[i]].client->sess.ghost )
  554.         {
  555.             if ( team != -1 && team != g_entities[level.sortedClients[i]].client->sess.ghost )
  556.             {
  557.                 continue;
  558.             }
  559.  
  560.             count ++;
  561.         }
  562.     }
  563.  
  564.     return count;
  565. }        
  566.  
  567. /*
  568. ================
  569. G_IsClientDead
  570.  
  571. Returns qtrue if the client is dead and qfalse if not
  572. ================
  573. */
  574. qboolean G_IsClientDead ( gclient_t* client )
  575. {
  576.     if ( client->ps.stats[STAT_HEALTH] <= 0 )
  577.     {
  578.         return qtrue;
  579.     }
  580.  
  581.     if ( client->ps.pm_type == PM_DEAD )
  582.     {
  583.         return qtrue;
  584.     }
  585.  
  586.     if ( client->sess.ghost )
  587.     {
  588.         return qtrue;
  589.     }
  590.  
  591.     return qfalse;
  592. }
  593.  
  594. /*
  595. ================
  596. G_IsClientSpectating
  597.  
  598. Returns qtrue if the client is spectating and qfalse if not
  599. ================
  600. */
  601. qboolean G_IsClientSpectating ( gclient_t* client )
  602. {
  603.     if ( client->pers.connected != CON_CONNECTED )
  604.     {
  605.         return qtrue;
  606.     }
  607.  
  608.     if ( client->sess.team == TEAM_SPECTATOR )
  609.     {
  610.         return qtrue;
  611.     }
  612.  
  613.     if ( client->sess.ghost )
  614.     {
  615.         return qtrue;
  616.     }
  617.  
  618.     return qfalse;
  619. }
  620.  
  621. /*
  622. ================
  623. TeamCount
  624.  
  625. Returns number of players on a team
  626. ================
  627. */
  628. int TeamCount( int ignoreClientNum, team_t team, int *alive ) 
  629. {
  630.     int        i;
  631.     int        count = 0;
  632.  
  633.     if ( alive )
  634.     {
  635.         *alive = 0;
  636.     }
  637.  
  638.     for ( i = 0 ; i < level.maxclients ; i++ ) 
  639.     {
  640.         if ( i == ignoreClientNum ) 
  641.         {
  642.             continue;
  643.         }
  644.         if ( level.clients[i].pers.connected == CON_DISCONNECTED ) 
  645.         {
  646.             continue;
  647.         }
  648.     
  649.         if ( level.clients[i].sess.team == team ) 
  650.         {
  651.             if ( !level.clients[i].sess.ghost && alive )
  652.             {
  653.                 (*alive)++;
  654.             }
  655.  
  656.             count++;
  657.         }
  658.     }
  659.  
  660.     return count;
  661. }
  662.  
  663. /*
  664. ================
  665. PickTeam
  666. ================
  667. */
  668. team_t PickTeam( int ignoreClientNum ) 
  669. {
  670.     int        counts[TEAM_NUM_TEAMS];
  671.  
  672.     counts[TEAM_BLUE] = TeamCount( ignoreClientNum, TEAM_BLUE, NULL );
  673.     counts[TEAM_RED] = TeamCount( ignoreClientNum, TEAM_RED, NULL );
  674.  
  675.     if ( counts[TEAM_BLUE] > counts[TEAM_RED] ) 
  676.     {
  677.         return TEAM_RED;
  678.     }
  679.  
  680.     if ( counts[TEAM_RED] > counts[TEAM_BLUE] ) 
  681.     {
  682.         return TEAM_BLUE;
  683.     }
  684.  
  685.     // equal team count, so join the team with the lowest score
  686.     if ( level.teamScores[TEAM_BLUE] > level.teamScores[TEAM_RED] ) 
  687.     {
  688.         return TEAM_RED;
  689.     }
  690.  
  691.     return TEAM_BLUE;
  692. }
  693.  
  694. /*
  695. ===========
  696. G_ClientCleanName
  697. ============
  698. */
  699. void G_ClientCleanName ( const char *in, char *out, int outSize, qboolean colors ) 
  700. {
  701.     int        len;
  702.     int        colorlessLen;
  703.     char    ch;
  704.     char    *p;
  705.     int        spaces;
  706.  
  707.     //save room for trailing null byte
  708.     outSize--;
  709.  
  710.     len = 0;
  711.     colorlessLen = 0;
  712.     p = out;
  713.     *p = 0;
  714.     spaces = 0;
  715.  
  716.     while( 1 ) 
  717.     {
  718.         ch = *in++;
  719.         if( !ch ) 
  720.         {
  721.             break;
  722.         }
  723.  
  724.         // don't allow leading spaces
  725.         if( !*p && ch == ' ' ) 
  726.         {
  727.             continue;
  728.         }
  729.  
  730.         // check colors
  731.         if( ch == Q_COLOR_ESCAPE ) 
  732.         {
  733.             // solo trailing carat is not a color prefix
  734.             if( !*in ) 
  735.             {
  736.                 break;
  737.             }
  738.  
  739.             if ( *in != Q_COLOR_ESCAPE )
  740.             {
  741.                 // don't allow black in a name, period
  742.                 if( !colors || ColorIndex(*in) == 0 ) 
  743.                 {
  744.                     in++;
  745.                     continue;
  746.                 }
  747.  
  748.                 // make sure room in dest for both chars
  749.                 if( len > outSize - 2 ) 
  750.                 {
  751.                     break;
  752.                 }
  753.  
  754.                 *out++ = ch;
  755.                 *out++ = *in++;
  756.                 len += 2;
  757.                 continue;
  758.             }
  759.             else
  760.             {
  761.                 *out++ = ch;
  762.                 *out++ = ch;
  763.                 in++;
  764.                 continue;
  765.             }
  766.         }
  767.  
  768.         // don't allow too many consecutive spaces
  769.         if( ch == ' ' ) 
  770.         {
  771.             spaces++;
  772.             if( spaces > 3 ) 
  773.             {
  774.                 continue;
  775.             }
  776.         }
  777.         else 
  778.         {
  779.             spaces = 0;
  780.         }
  781.  
  782.         if( len > outSize - 1 ) 
  783.         {
  784.             break;
  785.         }
  786.  
  787.         *out++ = ch;
  788.         colorlessLen++;
  789.         len++;
  790.     }
  791.  
  792.     *out = 0;
  793.  
  794.     // Trim whitespace off the end of the name
  795.     for ( out --; out >= p && (*out == ' ' || *out == '\t'); out -- )
  796.     {
  797.         *out = 0;
  798.     }
  799.  
  800.     // don't allow empty names
  801.     if( *p == 0 || colorlessLen == 0 ) 
  802.     {
  803.         Q_strncpyz( p, "UnnamedPlayer", outSize );
  804.     }
  805. }
  806.  
  807. /*
  808. ===========
  809. Updates the clients current outfittin
  810. ===========
  811. */
  812. void G_UpdateOutfitting ( int clientNum )
  813. {
  814.     gentity_t    *ent;
  815.     gclient_t    *client;
  816.     int            group;
  817.     int            ammoIndex;
  818.     int            idle;
  819.  
  820.     int            equipWeapon;
  821.     int            equipWeaponGroup;
  822.  
  823.     ent    = g_entities + clientNum;
  824.     client = ent->client;
  825.  
  826.     // No can do if 
  827.     if ( client->noOutfittingChange )
  828.     {
  829.         return;
  830.     }
  831.  
  832.     // Clear all ammo, clips, and weapons
  833.     client->ps.stats[STAT_WEAPONS] = 0;
  834.     memset ( client->ps.ammo, 0, sizeof(client->ps.ammo) );
  835.     memset ( client->ps.clip, 0, sizeof(client->ps.clip) );
  836.  
  837.     // Everyone gets some knives
  838.     client->ps.stats[STAT_WEAPONS] |= ( 1 << WP_KNIFE );
  839.     ammoIndex=weaponData[WP_KNIFE].attack[ATTACK_NORMAL].ammoIndex;
  840.     client->ps.clip[ATTACK_NORMAL][WP_KNIFE]=weaponData[WP_KNIFE].attack[ATTACK_NORMAL].clipSize;
  841.     client->ps.firemode[WP_KNIFE] = BG_FindFireMode ( WP_KNIFE, ATTACK_NORMAL, WP_FIREMODE_AUTO );
  842.  
  843.     if ( BG_IsWeaponAvailableForOutfitting ( WP_KNIFE, 2 ) )
  844.     {
  845.         client->ps.ammo[ammoIndex]=ammoData[ammoIndex].max;
  846.     }
  847.  
  848.     equipWeapon = WP_KNIFE;
  849.     equipWeaponGroup = OUTFITTING_GROUP_KNIFE;
  850.  
  851.     // Give all the outfitting groups to the player        
  852.     for ( group = 0; group < OUTFITTING_GROUP_ACCESSORY; group ++ )
  853.     {
  854.         gitem_t* item;
  855.         int         ammoIndex;
  856.         
  857.         // Nothing available in this group
  858.         if ( client->pers.outfitting.items[group] == -1 )
  859.         {
  860.             continue;
  861.         }
  862.  
  863.         // Grab the item that represents the weapon
  864.         item = &bg_itemlist[bg_outfittingGroups[group][client->pers.outfitting.items[group]]];
  865.  
  866.         client->ps.stats[STAT_WEAPONS] |= (1 << item->giTag);
  867.         ammoIndex = weaponData[item->giTag].attack[ATTACK_NORMAL].ammoIndex;
  868.         client->ps.ammo[ammoIndex] += weaponData[item->giTag].attack[ATTACK_NORMAL].extraClips * weaponData[item->giTag].attack[ATTACK_NORMAL].clipSize;
  869.         client->ps.clip[ATTACK_NORMAL][item->giTag] = weaponData[item->giTag].attack[ATTACK_NORMAL].clipSize;
  870.  
  871.         // Lower group numbers are bigger guns
  872.         if ( group < equipWeaponGroup )
  873.         {
  874.             equipWeaponGroup = group;
  875.             equipWeapon = item->giTag;
  876.         }
  877.  
  878.         // alt-fire ammo
  879.         ammoIndex = weaponData[item->giTag].attack[ATTACK_ALTERNATE].ammoIndex;
  880.         if ( weaponData[item->giTag].attack[ATTACK_ALTERNATE].fireAmount && AMMO_NONE != ammoIndex )
  881.         {
  882.             client->ps.clip[ATTACK_ALTERNATE][item->giTag] = weaponData[item->giTag].attack[ATTACK_ALTERNATE].clipSize;
  883.             client->ps.ammo[ammoIndex] += weaponData[item->giTag].attack[ATTACK_ALTERNATE].extraClips * weaponData[item->giTag].attack[ATTACK_ALTERNATE].clipSize;
  884.         }
  885.  
  886.         // Set the default firemode for this weapon
  887.         if ( client->ps.firemode[item->giTag] == WP_FIREMODE_NONE )
  888.         {
  889.             client->ps.firemode[item->giTag] = BG_FindFireMode ( item->giTag, ATTACK_NORMAL, WP_FIREMODE_AUTO );
  890.         }
  891.     }
  892.  
  893.     // Disable zooming
  894.     client->ps.zoomFov      = 0;
  895.     client->ps.zoomTime  = 0;
  896.     client->ps.pm_flags &= ~(PMF_ZOOM_FLAGS);
  897.  
  898.     client->ps.weapon = equipWeapon;
  899.     client->ps.weaponstate = WEAPON_READY; //WEAPON_SPAWNING;
  900.     client->ps.weaponTime = 0;
  901.     client->ps.weaponAnimTime = 0;
  902.  
  903.     // Bot clients cant use the spawning state
  904. #ifdef _SOF2_BOTS
  905.     if ( ent->r.svFlags & SVF_BOT )
  906.     {
  907.         client->ps.weaponstate = WEAPON_READY;
  908.     }
  909. #endif
  910.  
  911.     // Default to auto (or next available fire mode).
  912.     BG_GetInviewAnim(client->ps.weapon,"idle",&idle);
  913.     client->ps.weaponAnimId = idle;
  914.     client->ps.weaponAnimIdChoice = 0;
  915.     client->ps.weaponCallbackStep = 0;
  916.  
  917.     // Armor?
  918.     client->ps.stats[STAT_ARMOR]   = 0;
  919.     client->ps.stats[STAT_GOGGLES] = GOGGLES_NONE;
  920.     switch ( bg_outfittingGroups[OUTFITTING_GROUP_ACCESSORY][client->pers.outfitting.items[OUTFITTING_GROUP_ACCESSORY]] )
  921.     {
  922.         default:
  923.         case MODELINDEX_ARMOR:
  924.             client->ps.stats[STAT_ARMOR] = MAX_HEALTH;
  925.             break;
  926.  
  927.         case MODELINDEX_THERMAL:
  928.             client->ps.stats[STAT_GOGGLES] = GOGGLES_INFRARED;
  929.             break;
  930.  
  931.         case MODELINDEX_NIGHTVISION:
  932.             client->ps.stats[STAT_GOGGLES] = GOGGLES_NIGHTVISION;
  933.             break;
  934.     }
  935.  
  936.     // Stuff which grenade is being used into stats for later use by
  937.     // the backpack code
  938.     client->ps.stats[STAT_OUTFIT_GRENADE] = bg_itemlist[bg_outfittingGroups[OUTFITTING_GROUP_GRENADE][client->pers.outfitting.items[OUTFITTING_GROUP_GRENADE]]].giTag;
  939. }
  940.  
  941. /*
  942. ===========
  943. G_FindClientByName
  944.  
  945. Looks up a player by its case insensitive name
  946. ============
  947. */
  948. gclient_t* G_FindClientByName ( const char* name, int ignoreNum )
  949. {
  950.     int        i;
  951.  
  952.     for ( i = 0; i < level.numConnectedClients; i ++ )
  953.     {
  954.         gentity_t* ent = &g_entities[level.sortedClients[i]];
  955.  
  956.         if ( level.sortedClients[i] == ignoreNum )
  957.         {
  958.             continue;
  959.         }
  960.  
  961.         if ( Q_stricmp ( name, ent->client->pers.netname ) == 0 )
  962.         {
  963.             return ent->client;
  964.         }
  965.     }
  966.  
  967.     return NULL;
  968. }
  969.  
  970. /*
  971. ===========
  972. ClientUserInfoChanged
  973.  
  974. Called from ClientConnect when the player first connects and
  975. directly by the server system when the player updates a userinfo variable.
  976.  
  977. The game can override any of the settings and call trap_SetUserinfo
  978. if desired.
  979. ============
  980. */
  981. void ClientUserinfoChanged( int clientNum ) 
  982. {
  983.     gentity_t    *ent;
  984.     int            team;
  985.     int            health;
  986.     char        *s;
  987.     gclient_t    *client;
  988.     char        oldname[MAX_STRING_CHARS];
  989.     char        userinfo[MAX_INFO_STRING];
  990.     char        origname[MAX_NETNAME];
  991.     int            namecount = 1;
  992.     TIdentity    *oldidentity;
  993.  
  994.     ent = g_entities + clientNum;
  995.     client = ent->client;
  996.  
  997.     trap_GetUserinfo( clientNum, userinfo, sizeof( userinfo ) );
  998.  
  999.     // check for malformed or illegal info strings
  1000.     if ( !Info_Validate(userinfo) ) 
  1001.     {
  1002.         strcpy (userinfo, "\\name\\badinfo");
  1003.     }
  1004.  
  1005.     // check for local client
  1006.     s = Info_ValueForKey( userinfo, "ip" );
  1007.     if ( !strcmp( s, "localhost" ) ) 
  1008.     {
  1009.         client->pers.localClient = qtrue;
  1010.     }
  1011.  
  1012.     // check the item prediction
  1013.     s = Info_ValueForKey( userinfo, "cg_predictItems" );
  1014.     if ( !atoi( s ) ) 
  1015.     {
  1016.         client->pers.predictItemPickup = qfalse;
  1017.     } 
  1018.     else 
  1019.     {
  1020.         client->pers.predictItemPickup = qtrue;
  1021.     }
  1022.  
  1023.     // Is anti-lag turned on?
  1024.     s = Info_ValueForKey ( userinfo, "cg_antiLag" );
  1025.     client->pers.antiLag = atoi( s )?qtrue:qfalse;
  1026.  
  1027.     // Is auto-reload turned on?
  1028.     s = Info_ValueForKey ( userinfo, "cg_autoReload" );
  1029.     client->pers.autoReload = atoi( s )?qtrue:qfalse;
  1030.     if ( client->pers.autoReload )
  1031.     {
  1032.         client->ps.pm_flags |= PMF_AUTORELOAD;
  1033.     }
  1034.     else
  1035.     {
  1036.         client->ps.pm_flags &= ~PMF_AUTORELOAD;
  1037.     }
  1038.  
  1039.     // set name
  1040.     Q_strncpyz ( oldname, client->pers.netname, sizeof( oldname ) );
  1041.     s = Info_ValueForKey (userinfo, "name");
  1042.     G_ClientCleanName( s, client->pers.netname, sizeof(client->pers.netname), level.gametypeData->teams?qfalse:qtrue );
  1043.  
  1044.     if ( client->sess.team == TEAM_SPECTATOR ) 
  1045.     {
  1046.         if ( client->sess.spectatorState == SPECTATOR_SCOREBOARD ) 
  1047.         {
  1048.             Q_strncpyz( client->pers.netname, "scoreboard", sizeof(client->pers.netname) );
  1049.         }
  1050.     }
  1051.  
  1052.     // See if we need to find a new name because the one they chose is taken
  1053.     Q_strncpyz ( origname, client->pers.netname,  MAX_NETNAME - 5 );
  1054.     while ( G_FindClientByName ( client->pers.netname, clientNum ) )
  1055.     {
  1056.         Com_sprintf ( client->pers.netname, MAX_NETNAME, "%s(%d)", origname, namecount );
  1057.         namecount++;
  1058.     }
  1059.  
  1060.     // set max health
  1061.     health = atoi( Info_ValueForKey( userinfo, "handicap" ) );
  1062.  
  1063.     // bots set their team a few frames later
  1064.     if ( level.gametypeData->teams && (g_entities[clientNum].r.svFlags & SVF_BOT)) 
  1065.     {
  1066.         s = Info_ValueForKey( userinfo, "team" );
  1067.         if ( !Q_stricmp( s, "red" ) || !Q_stricmp( s, "r" ) ) 
  1068.         {
  1069.             team = TEAM_RED;
  1070.         } 
  1071.         else if ( !Q_stricmp( s, "blue" ) || !Q_stricmp( s, "b" ) ) 
  1072.         {
  1073.             team = TEAM_BLUE;
  1074.         } 
  1075.         else 
  1076.         {
  1077.             // pick the team with the least number of players
  1078.             team = PickTeam( clientNum );
  1079.         }
  1080.     }
  1081.     else 
  1082.     {
  1083.         team = client->sess.team;
  1084.     }
  1085.  
  1086.     // Enforce the identities
  1087.     oldidentity = client->pers.identity;
  1088.  
  1089.     if( level.gametypeData->teams ) 
  1090.     {
  1091.         s = Info_ValueForKey ( userinfo, "team_identity" );
  1092.  
  1093.         // Lookup the identity by name and if it cant be found then pick a random one
  1094.         client->pers.identity = BG_FindIdentity ( s );
  1095.  
  1096.         if ( team != TEAM_SPECTATOR )
  1097.         {
  1098.             // No identity or a team mismatch means they dont get to be that skin
  1099.             if ( !client->pers.identity || Q_stricmp ( level.gametypeTeam[team], client->pers.identity->mTeam ) )
  1100.             {
  1101.                 // Get first matching team identity
  1102.                 client->pers.identity = BG_FindTeamIdentity ( level.gametypeTeam[team], -1 );
  1103.             }
  1104.         }
  1105.         else
  1106.         {    
  1107.             // Spectators are going to have to choose one of the two team skins and
  1108.             // the chance of them having the proper one in team_identity is slim, so just
  1109.             // give them a model they may use later
  1110.             client->pers.identity = BG_FindTeamIdentity ( level.gametypeTeam[TEAM_RED], 0 );
  1111.         }
  1112.     } 
  1113.     else 
  1114.     {
  1115.         s = Info_ValueForKey ( userinfo, "identity" );
  1116.  
  1117.         // Lookup the identity by name and if it cant be found then pick a random one
  1118.         client->pers.identity = BG_FindIdentity ( s );
  1119.     }
  1120.  
  1121.     // If the identity wasnt in the list then just give them the first identity.  We could
  1122.     // be fancy here and give them a random one, but this way you get less unwanted models 
  1123.     // loaded
  1124.     if ( !client->pers.identity  )
  1125.     {
  1126.         client->pers.identity = &bg_identities[0];
  1127.     }
  1128.  
  1129.     // Report the identity change
  1130.     if ( client->pers.connected == CON_CONNECTED ) 
  1131.     {
  1132.         if ( client->pers.identity && oldidentity && client->pers.identity != oldidentity && team != TEAM_SPECTATOR )
  1133.         {
  1134.             trap_SendServerCommand( -1, va("print \"%s" S_COLOR_WHITE " has changed identities\n\"", client->pers.netname ) );
  1135.         }
  1136.  
  1137.         // If the client is changing their name then handle some delayed name changes
  1138.         if ( strcmp( oldname, client->pers.netname ) ) 
  1139.         {                
  1140.             // Dont let them change their name too much
  1141.             if ( level.time - client->pers.netnameTime < 5000 )
  1142.             {
  1143.                 trap_SendServerCommand ( client - &level.clients[0], "print \"You must wait 5 seconds before changing your name again.\n\"" );
  1144.                 strcpy ( client->pers.netname, oldname );
  1145.             }
  1146.             // voting clients cannot change their names
  1147.             else if ( (level.voteTime || level.voteExecuteTime) && strstr ( level.voteDisplayString, oldname ) )
  1148.             {
  1149.                 trap_SendServerCommand ( client - &level.clients[0], "print \"You are not allowed to change your name while there is an active vote against you.\n\"" );
  1150.                 strcpy ( client->pers.netname, oldname );
  1151.             }
  1152.             // If they are a ghost or spectating in an inf game their name is deferred
  1153.             else if ( level.gametypeData->respawnType == RT_NONE && (client->sess.ghost || G_IsClientDead ( client ) ) )
  1154.             {
  1155.                 trap_SendServerCommand ( client - &level.clients[0], "print \"Name changes while dead will be deferred until you spawn again.\n\"" );
  1156.                 strcpy ( client->pers.deferredname, client->pers.netname );
  1157.                 strcpy ( client->pers.netname, oldname );
  1158.             }
  1159.             else
  1160.             {
  1161.                 trap_SendServerCommand( -1, va("print \"%s renamed to %s\n\"", oldname, client->pers.netname) );
  1162.                 client->pers.netnameTime = level.time;
  1163.             }
  1164.         }
  1165.     }
  1166.  
  1167.     // Outfitting if pickups are disabled
  1168.     if ( level.pickupsDisabled )
  1169.     {
  1170.         // Parse out the new outfitting
  1171.         BG_DecompressOutfitting ( Info_ValueForKey ( userinfo, "outfitting" ), &client->pers.outfitting );
  1172.         G_UpdateOutfitting ( clientNum );
  1173.     }
  1174.  
  1175.     // send over a subset of the userinfo keys so other clients can
  1176.     // print scoreboards, display models, and play custom sounds
  1177.     if ( ent->r.svFlags & SVF_BOT ) 
  1178.     {
  1179.         s = va("n\\%s\\t\\%i\\identity\\%s\\skill\\%s",
  1180.             client->pers.netname, team, client->pers.identity->mName, 
  1181.             Info_ValueForKey( userinfo, "skill" ) );
  1182.     } 
  1183.     else 
  1184.     {
  1185.         s = va("n\\%s\\t\\%i\\identity\\%s",
  1186.                client->pers.netname, team, client->pers.identity->mName );
  1187.     }
  1188.  
  1189.     trap_SetConfigstring( CS_PLAYERS+clientNum, s );
  1190.  
  1191.     G_LogPrintf( "ClientUserinfoChanged: %i %s\n", clientNum, s );
  1192. }
  1193.  
  1194.  
  1195. /*
  1196. ===========
  1197. ClientConnect
  1198.  
  1199. Called when a player begins connecting to the server.
  1200. Called again for every map change or map restart.
  1201.  
  1202. The session information will be valid after exit.
  1203.  
  1204. Return NULL if the client should be allowed, otherwise return
  1205. a string with the reason for denial.
  1206.  
  1207. Otherwise, the client will be sent the current gamestate
  1208. and will eventually get to ClientBegin.
  1209.  
  1210. firstTime will be qtrue the very first time a client connects
  1211. to the server machine, but qfalse on map changes and map
  1212. restarts.
  1213. ============
  1214. */
  1215. char *ClientConnect( int clientNum, qboolean firstTime, qboolean isBot ) 
  1216. {
  1217.     char        *value;
  1218.     gclient_t    *client;
  1219.     char        userinfo[MAX_INFO_STRING];
  1220.     char        ip[128];
  1221.     gentity_t    *ent;
  1222.  
  1223.     ent = &g_entities[ clientNum ];
  1224.  
  1225.     trap_GetUserinfo( clientNum, userinfo, sizeof( userinfo ) );
  1226.  
  1227.     // check to see if they are on the banned IP list
  1228.     value = Info_ValueForKey (userinfo, "ip");
  1229.     Com_sprintf ( ip, sizeof(ip), value );
  1230.  
  1231.     // we don't check password for bots and local client
  1232.     // NOTE: local client <-> "ip" "localhost"
  1233.     //   this means this client is not running in our current process
  1234.     if ( !( isBot ) && (strcmp(ip, "localhost") != 0)) 
  1235.     {
  1236.         // check for a password
  1237.         value = Info_ValueForKey (userinfo, "password");
  1238.         if ( g_password.string[0] && Q_stricmp( g_password.string, "none" ) &&
  1239.             strcmp( g_password.string, value) != 0) 
  1240.         {
  1241.             return va("Invalid password: %s", value );
  1242.         }
  1243.     }
  1244.  
  1245.     // they can connect
  1246.     ent->client = level.clients + clientNum;
  1247.     client = ent->client;
  1248.  
  1249.     memset( client, 0, sizeof(*client) );
  1250.  
  1251.     client->pers.connected = CON_CONNECTING;
  1252.  
  1253.     client->sess.team = TEAM_SPECTATOR;
  1254.  
  1255.     // read or initialize the session data
  1256.     if ( firstTime || level.newSession ) 
  1257.     {
  1258.         G_InitSessionData( client, userinfo );
  1259.     }
  1260.     
  1261.     G_ReadSessionData( client );
  1262.  
  1263.     if( isBot ) 
  1264.     {
  1265.         ent->r.svFlags |= SVF_BOT;
  1266.         ent->inuse = qtrue;
  1267.         if( !G_BotConnect( clientNum, !firstTime ) ) 
  1268.         {
  1269.             return "BotConnectfailed";
  1270.         }
  1271.     }
  1272.  
  1273.     // get and distribute relevent paramters
  1274.     G_LogPrintf( "ClientConnect: %i - %s\n", clientNum, ip );
  1275.     ClientUserinfoChanged( clientNum );
  1276.  
  1277.     // don't do the "xxx connected" messages if they were caried over from previous level
  1278.     if ( firstTime ) 
  1279.     {
  1280.         trap_SendServerCommand( -1, va("print \"%s" S_COLOR_WHITE " is connecting...\n\"", client->pers.netname) );
  1281.  
  1282.         // Broadcast team change if not going to spectator
  1283.         if ( level.gametypeData->teams && client->sess.team != TEAM_SPECTATOR ) 
  1284.         {
  1285.             BroadcastTeamChange( client, -1 );
  1286.         }
  1287.     }
  1288.  
  1289.     // count current clients and rank for scoreboard
  1290.     CalculateRanks();
  1291.  
  1292.     // Make sure they are unlinked
  1293.     trap_UnlinkEntity ( ent );
  1294.  
  1295.     return NULL;
  1296. }
  1297.  
  1298. /*
  1299. ===========
  1300. ClientBegin
  1301.  
  1302. called when a client has finished connecting, and is ready
  1303. to be placed into the level.  This will happen every level load,
  1304. and on transition between teams, but doesn't happen on respawns
  1305. ============
  1306. */
  1307. void ClientBegin( int clientNum ) 
  1308. {
  1309.     gentity_t    *ent;
  1310.     gclient_t    *client;
  1311.     gentity_t    *tent;
  1312.     int            flags;
  1313.     int            spawncount;
  1314.  
  1315.     ent = g_entities + clientNum;
  1316.  
  1317.     client = level.clients + clientNum;
  1318.  
  1319.     if ( ent->r.linked ) 
  1320.     {
  1321.         trap_UnlinkEntity( ent );
  1322.     }
  1323.  
  1324.     // Run a gametype check just in case the game hasnt started yet
  1325.     if ( !level.gametypeStartTime )
  1326.     {
  1327.         CheckGametype ( );
  1328.     }
  1329.  
  1330.     G_InitGentity( ent );
  1331.     ent->touch = 0;
  1332.     ent->pain = 0;
  1333.     ent->client = client;
  1334.  
  1335.     client->pers.connected = CON_CONNECTED;
  1336.     client->pers.enterTime = level.time;
  1337.     client->pers.teamState.state = TEAM_BEGIN;
  1338.  
  1339.     // save eflags around this, because changing teams will
  1340.     // cause this to happen with a valid entity, and we
  1341.     // want to make sure the teleport bit is set right
  1342.     // so the viewpoint doesn't interpolate through the
  1343.     // world to the new position
  1344.     flags = client->ps.eFlags;
  1345.     spawncount = client->ps.persistant[PERS_SPAWN_COUNT];
  1346.     memset( &client->ps, 0, sizeof( client->ps ) );
  1347.     client->ps.eFlags = flags;
  1348.     client->ps.persistant[PERS_SPAWN_COUNT] = spawncount;
  1349.  
  1350.     // locate ent at a spawn point
  1351.     ClientSpawn( ent );
  1352.  
  1353.     if ( client->sess.team != TEAM_SPECTATOR ) 
  1354.     {
  1355.         // send event
  1356.         tent = G_TempEntity( ent->client->ps.origin, EV_PLAYER_TELEPORT_IN );
  1357.         tent->s.clientNum = ent->s.clientNum;
  1358.  
  1359.         trap_SendServerCommand( -1, va("print \"%s" S_COLOR_WHITE " entered the game\n\"", client->pers.netname) );
  1360.     }
  1361.     
  1362.     G_LogPrintf( "ClientBegin: %i\n", clientNum );
  1363.  
  1364.     // See if we should spawn as a ghost
  1365.     if ( client->sess.team != TEAM_SPECTATOR && level.gametypeData->respawnType == RT_NONE )
  1366.     {
  1367.         // If there are ghosts already then spawn as a ghost because
  1368.         // the game is already in progress.
  1369.         if ( (level.gametypeJoinTime && (level.time - level.gametypeJoinTime) > (g_roundjointime.integer * 1000)) )
  1370.         {
  1371.             G_StartGhosting ( ent );
  1372.         }
  1373.     }
  1374.  
  1375.     // count current clients and rank for scoreboard
  1376.     CalculateRanks();
  1377. }
  1378.  
  1379. /*
  1380. ===========
  1381. G_SelectClientSpawnPoint
  1382.  
  1383. Selects a spawn point for the given client entity
  1384. ============
  1385. */
  1386. gspawn_t* G_SelectClientSpawnPoint ( gentity_t* ent )
  1387. {
  1388.     gclient_t*    client = ent->client;
  1389.     gspawn_t*    spawnPoint;
  1390.  
  1391.         // find a spawn point
  1392.     // do it before setting health back up, so farthest
  1393.     // ranging doesn't count this client
  1394.     if ( client->sess.team == TEAM_SPECTATOR ) 
  1395.     {
  1396.         spawnPoint = G_SelectSpectatorSpawnPoint ( );
  1397.     }
  1398.     else 
  1399.     {
  1400.         if ( level.gametypeData->teams && level.gametypeData->respawnType != RT_NORMAL )
  1401.         {
  1402.             // Dont bother selecting a safe spawn on non-respawn games, the map creator should
  1403.             // have done this for us.
  1404.             if ( level.gametypeData->respawnType == RT_NONE )
  1405.             {
  1406.                 spawnPoint = G_SelectRandomSpawnPoint ( ent->client->sess.team );
  1407.             }
  1408.             else
  1409.             {
  1410.                 spawnPoint = G_SelectRandomSafeSpawnPoint ( ent->client->sess.team, 1500 );
  1411.             }
  1412.  
  1413.             if ( !spawnPoint )
  1414.             {
  1415.                 // don't spawn near other players if possible
  1416.                 spawnPoint = G_SelectRandomSpawnPoint ( ent->client->sess.team );
  1417.             }
  1418.  
  1419.             // Spawn at any deathmatch spawn, telefrag if needed
  1420.             if ( !spawnPoint )
  1421.             {
  1422.                 spawnPoint = G_SelectRandomSpawnPoint ( TEAM_FREE );
  1423.             }
  1424.         }
  1425.         else
  1426.         {
  1427.             // Try deathmatch spawns first
  1428.             spawnPoint = G_SelectRandomSafeSpawnPoint ( TEAM_FREE, 1500 );
  1429.  
  1430.             // If none found use any spawn
  1431.             if ( !spawnPoint )
  1432.             {
  1433.                 spawnPoint = G_SelectRandomSafeSpawnPoint ( -1, 1500 );
  1434.             }
  1435.  
  1436.             // Spawn at any deathmatch spawn, telefrag if needed
  1437.             if ( !spawnPoint )
  1438.             {
  1439.                 spawnPoint = G_SelectRandomSpawnPoint ( TEAM_FREE );
  1440.             }
  1441.  
  1442.             // Spawn at any gametype spawn, telefrag if needed
  1443.             if ( !spawnPoint )
  1444.             {
  1445.                 spawnPoint = G_SelectRandomSpawnPoint ( -1 );
  1446.             }
  1447.         }        
  1448.     }
  1449.  
  1450.     return spawnPoint;
  1451. }
  1452.  
  1453. /*
  1454. ===========
  1455. ClientSpawn
  1456.  
  1457. Called every time a client is placed fresh in the world:
  1458. after the first ClientBegin, and after each respawn
  1459. Initializes all non-persistant parts of playerState
  1460. ============
  1461. */
  1462.  
  1463. void ClientSpawn(gentity_t *ent) 
  1464. {
  1465.     int                    index;
  1466.     vec3_t                spawn_origin;
  1467.     vec3_t                spawn_angles;
  1468.     gclient_t            *client;
  1469.     int                    i;
  1470.     clientPersistant_t    saved;
  1471.     clientSession_t        savedSess;
  1472.     int                    persistant[MAX_PERSISTANT];
  1473.     gspawn_t            *spawnPoint;
  1474.     int                    flags;
  1475.     int                    savedPing;
  1476.     int                    eventSequence;
  1477.     char                userinfo[MAX_INFO_STRING];
  1478.     int                    start_ammo_type;
  1479.     int                    ammoIndex;
  1480.     int                    idle;
  1481.  
  1482.     index  = ent - g_entities;
  1483.     client = ent->client;
  1484.  
  1485.     // Where do we spawn?
  1486.     spawnPoint = G_SelectClientSpawnPoint ( ent );
  1487.     if ( spawnPoint )
  1488.     {
  1489.         VectorCopy ( spawnPoint->angles, spawn_angles );
  1490.         VectorCopy ( spawnPoint->origin, spawn_origin );
  1491.         spawn_origin[2] += 9;
  1492.     }
  1493.     else
  1494.     {
  1495.         SetTeam ( ent, "s", NULL );
  1496.         return;
  1497.     }
  1498.  
  1499.     client->pers.teamState.state = TEAM_ACTIVE;
  1500.  
  1501.     // toggle the teleport bit so the client knows to not lerp
  1502.     // and never clear the voted flag
  1503.     flags = ent->client->ps.eFlags & (EF_TELEPORT_BIT | EF_VOTED);
  1504.     flags ^= EF_TELEPORT_BIT;
  1505.  
  1506.     // clear everything but the persistant data
  1507.     saved = client->pers;
  1508.     savedSess = client->sess;
  1509.     savedPing = client->ps.ping;
  1510.     for ( i = 0 ; i < MAX_PERSISTANT ; i++ ) 
  1511.     {
  1512.         persistant[i] = client->ps.persistant[i];
  1513.     }
  1514.     eventSequence = client->ps.eventSequence;
  1515.  
  1516.     memset (client, 0, sizeof(*client)); 
  1517.  
  1518.     client->pers = saved;
  1519.     client->sess = savedSess;
  1520.     client->ps.ping = savedPing;
  1521.     client->lastkilled_client = -1;
  1522.  
  1523.     for ( i = 0 ; i < MAX_PERSISTANT ; i++ ) 
  1524.     {
  1525.         client->ps.persistant[i] = persistant[i];
  1526.     }
  1527.     client->ps.eventSequence = eventSequence;
  1528.     
  1529.     // increment the spawncount so the client will detect the respawn
  1530.     client->ps.persistant[PERS_SPAWN_COUNT]++;
  1531.     client->ps.persistant[PERS_TEAM] = client->sess.team;
  1532.     client->ps.persistant[PERS_ATTACKER] = ENTITYNUM_WORLD;
  1533.  
  1534.     client->airOutTime = level.time + 12000;
  1535.  
  1536.     trap_GetUserinfo( index, userinfo, sizeof(userinfo) );
  1537.  
  1538.     // clear entity values
  1539.     client->ps.eFlags = flags;
  1540.  
  1541.     ent->s.groundEntityNum = ENTITYNUM_NONE;
  1542.     ent->client = &level.clients[index];
  1543.     ent->takedamage = qtrue;
  1544.     ent->inuse = qtrue;
  1545.     ent->classname = "player";
  1546.     ent->r.contents = CONTENTS_BODY;
  1547.     ent->clipmask = MASK_PLAYERSOLID;
  1548.     ent->die = player_die;
  1549.     ent->waterlevel = 0;
  1550.     ent->watertype = 0;
  1551.     ent->flags = 0;
  1552.     
  1553.     VectorCopy (playerMins, ent->r.mins);
  1554.     VectorCopy (playerMaxs, ent->r.maxs);
  1555.  
  1556.     client->ps.clientNum = index;
  1557.  
  1558.     // Bring back the saved firemodes
  1559.     memcpy ( client->ps.firemode, client->pers.firemode, sizeof(client->ps.firemode) );
  1560.  
  1561.     //give default weapons
  1562.     client->ps.stats[STAT_WEAPONS] = ( 1 << WP_NONE );
  1563.  
  1564.        client->noOutfittingChange = qfalse;
  1565.  
  1566.     // Give the client their weapons depending on whether or not pickups are enabled
  1567.     if ( level.pickupsDisabled )
  1568.     {
  1569.         G_UpdateOutfitting ( ent->s.number );
  1570.  
  1571.         // Prevent the client from picking up a whole bunch of stuff
  1572.         client->ps.pm_flags |= PMF_LIMITED_INVENTORY;
  1573.     }
  1574.     else
  1575.     {
  1576.         // Knife.
  1577.         client->ps.stats[STAT_WEAPONS] |= ( 1 << WP_KNIFE );
  1578.         ammoIndex=weaponData[WP_KNIFE].attack[ATTACK_NORMAL].ammoIndex;
  1579.         client->ps.ammo[ammoIndex]=ammoData[ammoIndex].max;
  1580.         client->ps.clip[ATTACK_NORMAL][WP_KNIFE]=weaponData[WP_KNIFE].attack[ATTACK_NORMAL].clipSize;
  1581.         client->ps.firemode[WP_KNIFE] = BG_FindFireMode ( WP_KNIFE, ATTACK_NORMAL, WP_FIREMODE_AUTO );
  1582.  
  1583.         // Set up some weapons and ammo for the player.
  1584.         client->ps.stats[STAT_WEAPONS] |= ( 1 << WP_USSOCOM_PISTOL );
  1585.         start_ammo_type = weaponData[WP_USSOCOM_PISTOL].attack[ATTACK_NORMAL].ammoIndex;
  1586.         client->ps.ammo[start_ammo_type] = weaponData[WP_USSOCOM_PISTOL].attack[ATTACK_NORMAL].clipSize;
  1587.         client->ps.clip[ATTACK_NORMAL][WP_USSOCOM_PISTOL] = weaponData[WP_USSOCOM_PISTOL].attack[ATTACK_NORMAL].clipSize;
  1588.         client->ps.firemode[WP_USSOCOM_PISTOL] = BG_FindFireMode ( WP_USSOCOM_PISTOL, ATTACK_NORMAL, WP_FIREMODE_AUTO );
  1589.  
  1590.         // alt-fire ammo
  1591.         start_ammo_type = weaponData[WP_USSOCOM_PISTOL].attack[ATTACK_ALTERNATE].ammoIndex;
  1592.         if (AMMO_NONE != start_ammo_type)
  1593.         {
  1594.             client->ps.ammo[start_ammo_type] = ammoData[start_ammo_type].max;
  1595.         }
  1596.  
  1597.         // Everyone gets full armor in deathmatch
  1598.         client->ps.stats[STAT_ARMOR] = MAX_HEALTH;
  1599.     }
  1600.  
  1601.     client->ps.stats[STAT_HEALTH] = ent->health = MAX_HEALTH;
  1602.  
  1603.     G_SetOrigin( ent, spawn_origin );
  1604.     VectorCopy( spawn_origin, client->ps.origin );
  1605.  
  1606.     // the respawned flag will be cleared after the attack and jump keys come up
  1607.     client->ps.pm_flags |= PMF_RESPAWNED;
  1608.     if ( client->pers.autoReload )
  1609.     {
  1610.         client->ps.pm_flags |= PMF_AUTORELOAD;
  1611.     }
  1612.     else
  1613.     {
  1614.         client->ps.pm_flags &= ~PMF_AUTORELOAD;
  1615.     }
  1616.  
  1617.     trap_GetUsercmd( client - level.clients, &ent->client->pers.cmd );
  1618.     SetClientViewAngle( ent, spawn_angles );
  1619.  
  1620.     if ( ent->client->sess.team != TEAM_SPECTATOR ) 
  1621.     {
  1622.         G_KillBox( ent );
  1623.         trap_LinkEntity (ent);
  1624.  
  1625.         // force the base weapon up
  1626.         if ( !level.pickupsDisabled )
  1627.         {
  1628.             client->ps.weapon = WP_USSOCOM_PISTOL;
  1629.             client->ps.weaponstate = WEAPON_RAISING;
  1630.             client->ps.weaponTime = 500;
  1631.  
  1632.             // Default to auto (or next available fire mode).
  1633.             client->ps.firemode[client->ps.weapon] = BG_FindFireMode ( client->ps.weapon, ATTACK_NORMAL, WP_FIREMODE_AUTO );
  1634.             BG_GetInviewAnim(client->ps.weapon,"idle",&idle);                    
  1635.             client->ps.weaponAnimId = idle;
  1636.             client->ps.weaponAnimIdChoice = 0;
  1637.             client->ps.weaponCallbackStep = 0;
  1638.         }
  1639.     }
  1640.     else
  1641.     {
  1642.         client->ps.weapon = WP_KNIFE;
  1643.         BG_GetInviewAnim(client->ps.weapon,"idle",&idle);
  1644.         client->ps.weaponAnimId = idle;
  1645.     }
  1646.  
  1647.     // don't allow full run speed for a bit
  1648.     client->ps.pm_flags |= PMF_TIME_KNOCKBACK;
  1649.     client->ps.pm_time = 100;
  1650.  
  1651.     client->respawnTime = level.time;
  1652.     client->invulnerableTime = level.time;
  1653.     client->ps.eFlags |= EF_INVULNERABLE;
  1654.     client->inactivityTime = level.time + g_inactivity.integer * 1000;
  1655.     client->latched_buttons = 0;
  1656.  
  1657.     // set default animations
  1658.     client->ps.weaponstate = WEAPON_READY;
  1659.     client->ps.torsoAnim = -1;
  1660.     client->ps.legsAnim = LEGS_IDLE;
  1661.  
  1662.     // Not on a ladder
  1663.     client->ps.ladder = -1;
  1664.  
  1665.     // Not leaning
  1666.     client->ps.leanTime = LEAN_TIME;
  1667.  
  1668.     if ( level.intermissiontime ) 
  1669.     {
  1670.         MoveClientToIntermission( ent );
  1671.     } 
  1672.  
  1673.     // run a client frame to drop exactly to the floor,
  1674.     // initialize animations and other things
  1675.     client->ps.commandTime = level.time - 100;
  1676.     ent->client->pers.cmd.serverTime = level.time;
  1677.     ClientThink( ent-g_entities );
  1678.  
  1679.     // positively link the client, even if the command times are weird
  1680.     if ( ent->client->sess.team != TEAM_SPECTATOR ) 
  1681.     {
  1682.         BG_PlayerStateToEntityState( &client->ps, &ent->s, qtrue );
  1683.         VectorCopy( ent->client->ps.origin, ent->r.currentOrigin );
  1684.         trap_LinkEntity( ent );
  1685.     }
  1686.  
  1687.     // run the presend to set anything else
  1688.     ClientEndFrame( ent );
  1689.  
  1690.     // clear entity state values
  1691.     BG_PlayerStateToEntityState( &client->ps, &ent->s, qtrue );
  1692.  
  1693.     // Frozen?
  1694.     if ( level.gametypeDelayTime > level.time )
  1695.     {
  1696.         ent->client->ps.stats[STAT_FROZEN] = level.gametypeDelayTime - level.time;
  1697.     }
  1698.  
  1699.     // Handle a deferred name change
  1700.     if ( client->pers.deferredname[0] )
  1701.     {
  1702.         trap_SendServerCommand( -1, va("print \"%s" S_COLOR_WHITE " renamed to %s\n\"", client->pers.netname, client->pers.deferredname) );
  1703.         strcpy ( client->pers.netname, client->pers.deferredname );
  1704.         client->pers.deferredname[0] = '\0';
  1705.         client->pers.netnameTime = level.time;
  1706.         ClientUserinfoChanged ( client->ps.clientNum );
  1707.     }
  1708.  
  1709.     // Update the time when other people can join the game
  1710.     if ( !level.gametypeJoinTime && level.gametypeData->teams )
  1711.     {
  1712.         // As soon as both teams have people on them the counter starts
  1713.         if ( TeamCount ( -1, TEAM_RED, NULL ) && TeamCount ( -1, TEAM_BLUE, NULL ) )
  1714.         {
  1715.             level.gametypeJoinTime = level.time;
  1716.         }
  1717.     }
  1718. }
  1719.  
  1720.  
  1721. /*
  1722. ===========
  1723. ClientDisconnect
  1724.  
  1725. Called when a player drops from the server.
  1726. Will not be called between levels.
  1727.  
  1728. This should NOT be called directly by any game logic,
  1729. call trap_DropClient(), which will call this and do
  1730. server system housekeeping.
  1731. ============
  1732. */
  1733. void ClientDisconnect( int clientNum ) 
  1734. {
  1735.     gentity_t    *ent;
  1736.     gentity_t    *tent;
  1737.     int            i;
  1738.  
  1739.     // cleanup if we are kicking a bot that
  1740.     // hasn't spawned yet
  1741.     G_RemoveQueuedBotBegin( clientNum );
  1742.  
  1743.     ent = g_entities + clientNum;
  1744.     if ( !ent->client ) 
  1745.     {
  1746.         return;
  1747.     }
  1748.  
  1749.     // stop any following clients
  1750.     for ( i = 0 ; i < level.maxclients ; i++ ) 
  1751.     {
  1752.         if ( G_IsClientSpectating ( &level.clients[i] ) &&
  1753.              level.clients[i].sess.spectatorState == SPECTATOR_FOLLOW &&
  1754.              level.clients[i].sess.spectatorClient == clientNum ) 
  1755.         {
  1756.             G_StopFollowing( &g_entities[i] );
  1757.         }
  1758.     }
  1759.  
  1760.     // send effect if they were completely connected
  1761.     if ( ent->client->pers.connected == CON_CONNECTED && 
  1762.          !G_IsClientSpectating ( ent->client )  &&
  1763.          !G_IsClientDead ( ent->client ) )
  1764.     {
  1765.         tent = G_TempEntity( ent->client->ps.origin, EV_PLAYER_TELEPORT_OUT );
  1766.         tent->s.clientNum = ent->s.clientNum;
  1767.  
  1768.         // Dont drop weapons
  1769.         ent->client->ps.stats[STAT_WEAPONS] = 0;
  1770.  
  1771.         // Get rid of things that need to drop
  1772.         TossClientItems( ent );
  1773.     }
  1774.  
  1775.     G_LogPrintf( "ClientDisconnect: %i\n", clientNum );
  1776.  
  1777.     trap_UnlinkEntity (ent);
  1778.     ent->s.modelindex = 0;
  1779.     ent->inuse = qfalse;
  1780.     ent->classname = "disconnected";
  1781.     ent->client->pers.connected = CON_DISCONNECTED;
  1782.     ent->client->ps.persistant[PERS_TEAM] = TEAM_FREE;
  1783.     ent->client->sess.team = TEAM_FREE;
  1784.  
  1785.     trap_SetConfigstring( CS_PLAYERS + clientNum, "");
  1786.  
  1787.     CalculateRanks();
  1788.  
  1789. #ifdef _SOF2_BOTS
  1790.     if ( ent->r.svFlags & SVF_BOT ) 
  1791.     {
  1792.         BotAIShutdownClient( clientNum, qfalse );
  1793.     }
  1794. #endif
  1795. }
  1796.  
  1797. /*
  1798. ===========
  1799. G_UpdateClientAnimations
  1800.  
  1801. Updates the animation information for the client
  1802. ============
  1803. */
  1804. void G_UpdateClientAnimations ( gentity_t* ent )
  1805. {
  1806.     gclient_t*    client;
  1807.  
  1808.     client = ent->client;
  1809.     
  1810.     // Check for anim change in the legs
  1811.     if ( client->legs.anim != ent->s.legsAnim )
  1812.     {
  1813.         client->legs.anim = ent->s.legsAnim;
  1814.         client->legs.animTime = level.time;
  1815.     }
  1816.  
  1817.     // Check for anim change in the torso
  1818.     if ( client->torso.anim != ent->s.torsoAnim )
  1819.     {
  1820.         client->torso.anim = ent->s.torsoAnim;
  1821.         client->torso.animTime = level.time;
  1822.     }
  1823.  
  1824.     // Force the legs and torso to stay aligned for now to ensure the client
  1825.     // and server are in sync with the angles.  
  1826.     // TODO: Come up with a way to keep these in sync on both client and server
  1827.     client->torso.yawing   = qtrue;
  1828.     client->torso.pitching = qtrue;
  1829.     client->legs.yawing    = qtrue;
  1830.  
  1831.     // Calculate the real torso and leg angles
  1832.     BG_PlayerAngles ( ent->client->ps.viewangles, 
  1833.                       NULL, 
  1834.                       
  1835.                       ent->client->ghoulLegsAngles,
  1836.                       ent->client->ghoulLowerTorsoAngles,
  1837.                       ent->client->ghoulUpperTorsoAngles,
  1838.                       ent->client->ghoulHeadAngles,
  1839.  
  1840.                       (float)(ent->client->ps.leanTime - LEAN_TIME) / LEAN_TIME * LEAN_OFFSET,
  1841.                      
  1842.                       0, 
  1843.                       0, 
  1844.                       level.time,
  1845.                      
  1846.                       &client->torso,
  1847.                       &client->legs,
  1848.                      
  1849.                       level.time - level.previousTime, 
  1850.                      
  1851.                       client->ps.velocity,
  1852.  
  1853.                       qfalse, 
  1854.                       ent->s.angles2[YAW],
  1855.                       NULL );
  1856. }
  1857.  
  1858. /*
  1859. ===========
  1860. G_FindNearbyClient
  1861.  
  1862. Locates a client thats near the given origin
  1863. ============
  1864. */
  1865. gentity_t* G_FindNearbyClient ( vec3_t origin, team_t team, float radius, gentity_t* ignore )
  1866. {
  1867.     int i;
  1868.  
  1869.     for ( i = 0; i < level.numConnectedClients; i ++ )
  1870.     {
  1871.         gentity_t* other = &g_entities[ level.sortedClients[i] ];
  1872.         vec3_t       diff;
  1873.  
  1874.         if ( other->client->pers.connected != CON_CONNECTED )
  1875.         {
  1876.             continue;
  1877.         }
  1878.  
  1879.         if ( other == ignore )
  1880.         {
  1881.             continue;
  1882.         }
  1883.  
  1884.         if ( G_IsClientSpectating ( other->client ) || G_IsClientDead ( other->client ) )
  1885.         {
  1886.             continue;
  1887.         }
  1888.  
  1889.         if ( other->client->sess.team != team )
  1890.         {
  1891.             continue;
  1892.         }
  1893.  
  1894.         // See if this client is close enough to yell sniper
  1895.         VectorSubtract ( other->r.currentOrigin, origin, diff );
  1896.         if ( VectorLengthSquared ( diff ) < radius * radius )
  1897.         {
  1898.             return other;
  1899.         }
  1900.     }
  1901.  
  1902.     return NULL;
  1903. }
  1904.